#include "CmdChannel.h"
#include "Manager.h"
#include "common.h"
#include "JsonAnalysis.h"

CCmdChannel::CCmdChannel(CManager *pMan)
: m_pClient(NULL)
, m_bExit(false)
, m_pMan(pMan)
, m_bConnect(false)
, m_bValid(false)
, m_usPacketNum(0)
, m_bFirstConnect(false)
{
	m_pWaitList = new CWaiterList;	
}

CCmdChannel::~CCmdChannel(void)
{
	Stop();
}

bool CCmdChannel::Start(void)
{
	m_bExit = false;
	m_bConnect = false;

	return StartThread();
}

void CCmdChannel::Stop(void)
{
	m_bExit = true;

	WaitForStop();

	if (m_pClient)
	{
		delete m_pClient;
		m_pClient = NULL;
	}

	while (m_lstRecvData.size())
	{
		LPRecvDataInfo pRecvData = NULL;

		{
			CAutoLock autoLock(&m_lockRecvData);
			pRecvData = (LPRecvDataInfo)m_lstRecvData.front();
			m_lstRecvData.pop_front();
		}

		if (pRecvData)
			ClearRecvData(pRecvData);			
	}	
}

bool CCmdChannel::GetValid(void)
{
	return m_bValid;
}

bool CCmdChannel::SendChatMessage(const char *pChatMessage)
{
	bool bRet = false;
	string strJson = pChatMessage;

	if (SendData(CMD_T2S_CHATMESSAGE, strJson.c_str(), strJson.length()) == strJson.length())
		bRet = true;

	return bRet;
}

int CCmdChannel::StartWbRequest(int nUserId, char *outMsg, int *len)
{
	int nRet = -1;
	string strJson;

	CJsonAnalysis::TerminalStartWb(strJson, nUserId);

	unsigned long param;
	if (SendDataEx(CMD_T2S_WHITEBOARD_REQ, strJson.c_str(), strJson.length(), param))
	{
		LPRspInfo pRspInfo = (LPRspInfo)param;
		if (pRspInfo)
		{
			if (pRspInfo->pData)
			{
				nRet = 0;

				memset(outMsg, 0x00, *len);
				if (*len >= pRspInfo->nLen)
				{						
					memcpy(outMsg, pRspInfo->pData, pRspInfo->nLen);
					*len = pRspInfo->nLen;
				}
				else
				{
					memcpy(outMsg, pRspInfo->pData, *len);
					*len = pRspInfo->nLen;
				}					

				delete [] pRspInfo->pData;
				pRspInfo->pData = NULL;
			}

			delete pRspInfo;
			pRspInfo = NULL;				
		}				
	}

	return nRet;
}

int CCmdChannel::CloseWb(int nUserId)
{
	int nRet = -1;
	string strJson;

	CJsonAnalysis::TerminalCloseWb(strJson, nUserId);

	if (SendData(CMD_T2S_WHITEBOARD_CLOSE, strJson.c_str(), strJson.length()) == strJson.length())
		nRet = 0;

	return nRet;
}

int CCmdChannel::VoteMsg(const char *pMsg, int nLen)
{
	int nRet = -1;
	string strJson = pMsg;

	if (SendData(CMD_T2S_VOTE, strJson.c_str(), strJson.length()) == strJson.length())
		nRet = 0;

	return nRet;
}

int CCmdChannel::CenterControl(int nControlType)
{
	int nRet = -1;
	string strJson;

	CJsonAnalysis::CenterControlMsg(strJson, nControlType);

	if (SendData(CMD_T2S_CENTERCONTROL, strJson.c_str(), strJson.length()) == strJson.length())
		nRet = 0;

	return nRet;
}

bool CCmdChannel::OpenScreenBroadcast()
{
	bool bRet = false;
	string strJson;

	CJsonAnalysis::ScreenBroadcastReq(strJson, m_pMan->GetTerminalId());

	unsigned long param;
	if (SendDataEx(CMD_T2S_REQUESTSCREENCAST_REQ, strJson.c_str(), strJson.length(), param))
	{
		LPRspInfo pRspInfo = (LPRspInfo)param;
		if (pRspInfo)
		{
			if (pRspInfo->pData)
			{
				int nResult = -1;
				CJsonAnalysis::AnalysScreenBroadcastRsp(pRspInfo->pData, nResult);

				if (nResult == 1)
					bRet = true;				

				delete [] pRspInfo->pData;
				pRspInfo->pData = NULL;
			}

			delete pRspInfo;
			pRspInfo = NULL;				
		}
	}

	return bRet;
}

void CCmdChannel::ThreadProcMain(void)
{	
	while (!m_bExit)
	{
		unsigned long ulNow = GetTimeStamp();

		while (m_lstRecvData.size() && m_bConnect && !m_bExit)
		{
			LPRecvDataInfo pRecvData = NULL;

			{
				CAutoLock autoLock(&m_lockRecvData);
				pRecvData = (LPRecvDataInfo)m_lstRecvData.front();
				m_lstRecvData.pop_front();
			}

			if (pRecvData)
				HandleRecvData(pRecvData);			
		}

		if (!m_bConnect && (ulNow - m_ulLastConnectTime) >= MAX_SEND_INTERVAL * 1000)
		{
			ConnectServer();
			m_ulLastConnectTime = ulNow;			
		}

		if (m_bConnect && (ulNow - m_ulLastSendTime) >= MAX_SEND_INTERVAL * 1000)
		{
			SendHeartbetMsg();
			m_ulLastSendTime = ulNow;
		}

		my_sleep(2);
	}
}

void CCmdChannel::OnRecvTcpServerDataNotify(const int nCmd, const int nPacketNum, const int nSendId, const char *szBuf, int nLen)
{
	LPRecvDataInfo pRecvData = new RecvDataInfo;
	if (pRecvData)
	{
		pRecvData->nSendId = nSendId;
		pRecvData->nMsgLen = nLen - HEADER_PACKET_LEN;
		pRecvData->nCmd = nCmd;
		pRecvData->nPacketNum = nPacketNum;

		if (pRecvData->nMsgLen > 0)
		{
			pRecvData->nMsgLen += 1;
			pRecvData->pData = new char[pRecvData->nMsgLen];			

			if (!pRecvData->pData)
			{
				delete pRecvData;
				pRecvData = NULL;

				return;
			}
			
			memset(pRecvData->pData, 0x00, pRecvData->nMsgLen);
			memcpy(pRecvData->pData, szBuf + HEADER_PACKET_LEN, pRecvData->nMsgLen -1);
		}		

		CAutoLock autoLock(&m_lockRecvData);
		m_lstRecvData.push_back(pRecvData);
	}
}

void CCmdChannel::OnDisConnTcpServerNotify(const int nType)
{
	m_bConnect = false;
	m_bValid = false;
	m_bFirstConnect = false;

	m_ulLastConnectTime = GetTimeStamp();

	RecvDataInfo *pRecvInfo = new RecvDataInfo;
	pRecvInfo->nCmd = CMDCHANNEL_BREAK;

	m_pMan->PutRecvData(pRecvInfo);
}

int CCmdChannel::ConnectServer(void)
{
	if (m_pClient)
	{
		delete m_pClient;
		m_pClient = NULL;
	}

	m_pClient = new CSocketTcpClient(this, true);

	if (!m_pClient)
		return -1;

	if (m_pClient->StartConnect(m_pMan->GetSvrIp(), m_pMan->GetSvrPort()))
	{
		m_ulLastSendTime = GetTimeStamp();	
		m_bConnect = true;

		SendChannelDeclare();

		return 0;
	}	

	if (!m_bFirstConnect)
	{
		m_bFirstConnect = true;

		RecvDataInfo *pRecvInfo = new RecvDataInfo;
		pRecvInfo->nCmd = CMDCHANNEL_CONNECT_FAIL;

		m_pMan->PutRecvData(pRecvInfo);
	}

	return -1;
}

void CCmdChannel::HandleRecvData(RecvDataInfo *pRecvData)
{
	if (pRecvData)
	{
		switch (pRecvData->nCmd)
		{
		case CMD_T2S_CHANNELDECLAR_RSP:
			HandleChannelDeclareRsp(pRecvData);
			ClearRecvData(pRecvData);
			break;
		case CMD_T2S_LOGIN_RSP:
		case CMD_T2S_USERINFO_RSP:
		case CMD_T2S_USERLIST_RSP:
		case CMD_T2S_FILELIST_RSP:
		case CMD_T2S_ISSUELIST_RSP:
		case CMD_T2S_GETVOTE_RSP:
		case CMD_T2S_REQUESTSCREENCAST_RSP:
		case CMD_T2S_WHITEBOARD_RSP:
			HandleRspMsg(pRecvData);
			ClearRecvData(pRecvData);
			break;
		case CMD_S2T_MEETINGINFO:
		case CMD_T2S_STARTSCREENCAST:
		case CMD_T2S_STOPSCREENCAST:
		case CMD_T2S_REQUESTSCREENCAST_REQ:
		case CMD_S2T_FILEUPDATENOTICE:
		case CMD_S2T_VOTEUPDATE:
		case CMD_T2S_SIGNCONTROL:
		case CMD_T2S_INVITESCREENCAST_REQ:
		case CMD_S2T_USERSTATUS:
		case CMD_S2T_MESSAGE:
		case CMD_S2T_ISSUEUPDATE:
		case CMD_S2T_ISSUENOTICE:
		case CMD_S2T_WHITEBOARD_NOTICE:
		case CMD_S2T_SIGNSTATUS:
		case CMD_S2T_STARTVOTE:
		case CMD_S2T_STOPVOTE:
		case CMD_S2T_VOTESTATUS:
		case CMD_S2T_VOTERESULT_NOTICE:
		case CMD_T2S_CENTERCONTROL:
		case CMD_S2T_EXTSIGNAL_CONTROL:
			m_pMan->PutRecvData(pRecvData);
			break;
		default:
			ClearRecvData(pRecvData);
			break;
		}		
	}
}

int CCmdChannel::SendData(int nCmd, const char *pData, int nDataLen)
{
	if (!m_bConnect)
		return -1;

	m_ulLastSendTime = GetTimeStamp();
	return m_pClient->SendData(m_pMan->GetTerminalId(), nCmd, m_usPacketNum++, pData, nDataLen);
}

bool CCmdChannel::SendDataEx(int nCmd, const char *pData, int nDataLen, unsigned long &param)
{
	int packetNum;
	char szKey[20] = { 0 };

	if (!m_bConnect)
		return false;

	packetNum = m_usPacketNum++;	
	sprintf_s(szKey, "%d", packetNum);
	string strKey = szKey;
	int nIndex = m_pWaitList->AddWaitItem(strKey);

	m_ulLastSendTime = GetTimeStamp();

	int nRet = m_pClient->SendData(m_pMan->GetTerminalId(), nCmd, packetNum, pData, nDataLen);
	if (nRet == nDataLen)
	{
		if (m_pWaitList->Wait(nIndex, MAX_TIMEOUT_LEN, param))
			return true;
	}	

	return false;
}

void CCmdChannel::HandleRspMsg(RecvDataInfo *pRecvData)
{
	char szKey[20] = { 0 };
	sprintf_s(szKey, "%d", pRecvData->nPacketNum);
	string strKey = szKey;

	RspInfo *pRspData = NULL;
	if (pRecvData->pData && pRecvData->nMsgLen > 0)
	{
		pRspData = new RspInfo;
		if (pRspData)
		{
			int nDataLen = pRecvData->nMsgLen + 1;
			pRspData->pData = new char[nDataLen];
			pRspData->nLen = nDataLen;

			memset(pRspData->pData, 0x00, nDataLen);
			memcpy(pRspData->pData, pRecvData->pData, pRecvData->nMsgLen);
		}
	}

	if (!m_pWaitList->Signal(szKey, (unsigned long)pRspData))
	{
		if (pRspData)
		{
			if (pRspData->pData)
			{
				delete[] pRspData->pData;
				pRspData->pData = NULL;
			}

			delete pRspData;
			pRspData = NULL;
		}		
	}
}

void CCmdChannel::HandleChannelDeclareRsp(RecvDataInfo *pRecvData)
{
	int nResult(0);
	string strError;

	if (CJsonAnalysis::AnalysChannelDeclareRsp(pRecvData->pData, nResult, strError))
	{
		if (nResult == 200)
		{
			RecvDataInfo *pRecvInfo = new RecvDataInfo;
			pRecvInfo->nCmd = CMDCHANNEL_CONNECT;

			m_pMan->PutRecvData(pRecvInfo);

			m_bValid = true;
		}
	}
}

bool CCmdChannel::SendChannelDeclare(void)
{
	string strJson; 
	if (CJsonAnalysis::ChannelDeclare(strJson, CMD_CHANNEL, CODEC, 0))
	{
		int nLen = strJson.length();
		if (SendData(CMD_T2S_CHANNELDECLAR_REQ, strJson.c_str(), nLen) == nLen)
			return true;
	}

	return false;
}

bool CCmdChannel::SendHeartbetMsg(void)
{
	string strJson; 
	if (CJsonAnalysis::Heartbet(strJson))
	{
		int nLen = strJson.length();
		if (SendData(CMD_HEARTBEAT_REQ, strJson.c_str(), nLen) == nLen)
			return true;
	}

	return false;
}

void CCmdChannel::ClearRecvData(RecvDataInfo *pRecvData)
{
	if (pRecvData)
	{
		if (pRecvData->pData)
		{
			delete [] pRecvData->pData;
			pRecvData->pData = NULL;
		}

		delete pRecvData;
		pRecvData = NULL;
	}
}
